﻿using System;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SpringGraph.Controls;
using System.Collections.Generic;

namespace SpringGraph.Extensions
{
    public static class UIElementExtensions
    {
        public static bool IsMouseInside(this Control element, MouseButtonEventArgs e)
        {
            var point = e.GetPosition(element);

            if ((point.X > 0 && point.X < element.GetRealWidth()) &&
                (point.Y > 0 && point.Y < element.GetRealHeight()))
            {
                return true;
            }

            return false;
        }

        public static T ParentOfType<T>(this FrameworkElement element)
            where T : FrameworkElement
        {
            if (element is T)
                return (T)element;
            else if (element.Parent != null && element.Parent is FrameworkElement)
                return ((FrameworkElement)element.Parent).ParentOfType<T>();
            else return null;
        }

        public static List<T> ChildrenOfType<T>(this Panel element)
            where T : FrameworkElement
        {
            List<T> results = new List<T>();

            foreach(var child in element.Children)
            {
                if (child is T)
                    results.Add((T)child);
            }

            return results;
        }

        public static Point GetPositionCenter(this FrameworkElement element)
        {
            var point = element.GetPosition();
            return GetCenter(element, point);
        }

        public static Point GetPositionCenter(this FrameworkElement element, UIElement container)
        {
            var point = element.GetPosition(container);
            return GetCenter(element, point);
        }

        public static Point GetPositionRightBottom(this FrameworkElement element, UIElement container)
        {
            var point = element.GetPosition(container);

            point.X += element.GetRealWidth();
            point.Y += element.GetRealHeight();

            return point;
        }

        private static Point GetCenter(FrameworkElement element, Point point)
        {
            point.X += element.GetRealWidth() / 2;
            point.Y += element.GetRealHeight() / 2;
            return point;
        }

        public static void SetX(this UIElement element, double x)
        {
            Canvas.SetLeft(element, x);
        }

        public static void SetY(this UIElement element, double y)
        {
            Canvas.SetTop(element, y);
        }

        public static void SetZIndex(this UIElement element, int z)
        {
            Canvas.SetZIndex(element, z);
        }

        public static double GetX(this UIElement element)
        {
            return Canvas.GetLeft(element);
        }

        public static double GetY(this UIElement element)
        {
            return Canvas.GetTop(element);
        }

        public static int GetZIndex(this UIElement element)
        {
            return Canvas.GetZIndex(element);
        }

        public static Point GetPosition(this FrameworkElement element, UIElement container)
        {
            Point absolute = new Point();

            try
            {
                absolute = element.TransformToVisual(container).Transform(new Point(0, 0));
            }
            catch { }

            return absolute;
        }

        public static Point GetPosition(this FrameworkElement element)
        {
            return element.GetPosition(Application.Current.RootVisual);
        }

        public static Rect GetCurrentRect(this FrameworkElement element, UIElement container)
        {
            Point absolute = element.GetPosition(container);

            var rect = new Rect()
            {
                X = absolute.X,
                Y = absolute.Y,
                Width = element.GetRealWidth(),
                Height = element.GetRealHeight()
            };

            return rect;
        }

        public static double GetRealWidth(this FrameworkElement element)
        {
            var width = element.Width;

            if (double.IsNaN(width))
                width = element.ActualWidth;

            var scales = element.GetScaleTransforms();

            if (scales.Count > 0)
            {
                double scaleX = 1;
                scales.ForEach(scale => scaleX = scaleX * scale.ScaleX);
                width = width * scaleX;
            }

            return width;
        }

        public static double GetRealHeight(this FrameworkElement element)
        {
            var height = element.Height;

            if (double.IsNaN(height))
                height = element.ActualHeight;

            var scales = element.GetScaleTransforms();

            if (scales.Count > 0)
            {
                double scaleY = 1;
                scales.ForEach(scale => scaleY = scaleY * scale.ScaleY);
                height = height * scaleY;
            }

            return height;
        }

        public static List<ScaleTransform> GetScaleTransforms(this FrameworkElement element)
        {
            List<ScaleTransform> transforms = new List<ScaleTransform>();

            if (element.RenderTransform != null && (element.RenderTransform is ScaleTransform
                || element.RenderTransform is TransformGroup && ((TransformGroup)element.RenderTransform).Children.OfType<ScaleTransform>().Count() > 0))
            {
                var scale = element.RenderTransform as ScaleTransform;
                if (scale == null)
                    scale = ((TransformGroup)element.RenderTransform).Children.OfType<ScaleTransform>().FirstOrDefault();

                transforms.Add(scale);
            }

            if (element.Parent != null)
            {
                transforms.AddRange(((FrameworkElement)element.Parent).GetScaleTransforms());
            }

            return transforms;
        }
    }
}
